#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
#else
#define _XOPEN_SOURCE 600 // For flockfile() on Linux
#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
#define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
#endif

#if defined(__SYMBIAN32__)
#define NO_SSL // SSL is not supported
#define NO_CGI // CGI is not supported
#define PATH_MAX FILENAME_MAX
#endif // __SYMBIAN32__

#ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#endif // !_WIN32_WCE

#include <time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>

#if defined(_WIN32) && !defined(__SYMBIAN32__) // Windows specific
#define _WIN32_WINNT 0x0400 // To make it link in VS2005
#include <windows.h>

#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif

#ifndef _WIN32_WCE
#include <process.h>
#include <direct.h>
#include <io.h>
#else // _WIN32_WCE
#include <winsock2.h>
#define NO_CGI // WinCE has no pipes

typedef long off_t;
#define BUFSIZ  4096

#define errno   GetLastError()
#define strerror(x)  _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
#endif // _WIN32_WCE

#define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \
            ((uint64_t)((uint32_t)(hi))) << 32))
#define RATE_DIFF 10000000 // 100 nsecs
#define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de)
#define SYS2UNIX_TIME(lo, hi) \
    (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)

// Visual Studio 6 does not know __func__ or __FUNCTION__
// The rest of MS compilers use __FUNCTION__, not C99 __func__
// Also use _strtoui64 on modern M$ compilers
#if defined(_MSC_VER) && _MSC_VER < 1300
#define STRX(x) #x
#define STR(x) STRX(x)
#define __func__ "line " STR(__LINE__)
#define strtoull(x, y, z) strtoul(x, y, z)
#define strtoll(x, y, z) strtol(x, y, z)
#else
#define __func__  __FUNCTION__
#define strtoull(x, y, z) _strtoui64(x, y, z)
#define strtoll(x, y, z) _strtoi64(x, y, z)
#endif // _MSC_VER

#define ERRNO   GetLastError()
#define NO_SOCKLEN_T
#define SSL_LIB   "ssleay32.dll"
#define CRYPTO_LIB  "libeay32.dll"
#define DIRSEP '\\'
#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')
#define O_NONBLOCK  0
#if !defined(EWOULDBLOCK)
#define EWOULDBLOCK  WSAEWOULDBLOCK
#endif // !EWOULDBLOCK
#define _POSIX_
#define INT64_FMT  "I64d"

#define WINCDECL __cdecl
#define SHUT_WR 1
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define sleep(x) Sleep((x) * 1000)

#define pipe(x) _pipe(x, BUFSIZ, _O_BINARY)
#define popen(x, y) _popen(x, y)
#define pclose(x) _pclose(x)
#define close(x) _close(x)
#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
#define RTLD_LAZY  0
#define fseeko(x, y, z) fseek((x), (y), (z))
#define fdopen(x, y) _fdopen((x), (y))
#define write(x, y, z) _write((x), (y), (unsigned) z)
#define read(x, y, z) _read((x), (y), (unsigned) z)
#define flockfile(x) (void) 0
#define funlockfile(x) (void) 0

#if !defined(fileno)
#define fileno(x) _fileno(x)
#endif // !fileno MINGW #defines fileno

typedef HANDLE pthread_mutex_t;
typedef struct {HANDLE signal, broadcast;} pthread_cond_t;
typedef DWORD pthread_t;
#define pid_t HANDLE // MINGW typedefs pid_t to int. Using #define here.

struct timespec {
    long tv_nsec;
    long tv_sec;
};

static int pthread_mutex_lock(pthread_mutex_t *);
static int pthread_mutex_unlock(pthread_mutex_t *);
static FILE *mg_fopen(const char *path, const char *mode);

#if defined(HAVE_STDINT)
#include <stdint.h>
#else
typedef unsigned int  uint32_t;
typedef unsigned short  uint16_t;
typedef unsigned __int64 uint64_t;
typedef __int64   int64_t;
#define INT64_MAX  9223372036854775807
#endif // HAVE_STDINT

// POSIX dirent interface
struct dirent {
    char d_name[PATH_MAX];
};

typedef struct DIR {
    HANDLE   handle;
    WIN32_FIND_DATAW info;
    struct dirent  result;
} DIR;

#else    // UNIX  specific
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <stdint.h>
#include <inttypes.h>
#include <netdb.h>

#include <pwd.h>
#include <unistd.h>
#include <dirent.h>
#if !defined(NO_SSL_DL) && !defined(NO_SSL)
#include <dlfcn.h>
#endif
#include <pthread.h>
#if defined(__MACH__)
#define SSL_LIB   "libssl.dylib"
#define CRYPTO_LIB  "libcrypto.dylib"
#else
#if !defined(SSL_LIB)
#define SSL_LIB   "libssl.so"
#endif
#if !defined(CRYPTO_LIB)
#define CRYPTO_LIB  "libcrypto.so"
#endif
#endif
#define DIRSEP   '/'
#define IS_DIRSEP_CHAR(c) ((c) == '/')
#ifndef O_BINARY
#define O_BINARY  0
#endif // O_BINARY
#define closesocket(a) close(a)
#define mg_fopen(x, y) fopen(x, y)
#define mg_mkdir(x, y) mkdir(x, y)
#define mg_remove(x) remove(x)
#define mg_rename(x, y) rename(x, y)
#define ERRNO errno
#define INVALID_SOCKET (-1)
#define INT64_FMT PRId64
typedef int SOCKET;
#define WINCDECL

#endif // End of Windows and UNIX specific includes

#include "mongoose.h"

#define MONGOOSE_VERSION "3.0"
#define PASSWORDS_FILE_NAME ".htpasswd"
#define CGI_ENVIRONMENT_SIZE 4096
#define MAX_CGI_ENVIR_VARS 64
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))

#if defined(DEBUG)
#define debug(...) do { \
    flockfile(stdout); \
    printf("###%lu.%p.%s: ", \
            (unsigned long) time(NULL) - start_time, (void *) pthread_self(), \
            __func__); \
    printf(__VA_ARGS__); \
    putchar('\n'); \
    fflush(stdout); \
    funlockfile(stdout); \
} while (0)
#else
#define debug(...)
#endif // DEBUG

#if defined(DEBUG_TRACE)
#define DEBUG_TRACE(x) do { \
    flockfile(stdout); \
    printf("###%lu.%p.%s: ", \
            (unsigned long) time(NULL) - start_time, (void *) pthread_self(), \
            __func__); \
    printf x; \
    putchar('\n'); \
    fflush(stdout); \
    funlockfile(stdout); \
} while (0)
#else
#define DEBUG_TRACE(x)
#endif // DEBUG

// Darwin prior to 7.0 and Win32 do not have socklen_t
#ifdef NO_SOCKLEN_T
typedef int socklen_t;
#endif // NO_SOCKLEN_T

typedef void * (*mg_thread_func_t)(void *);

static const char *http_500_error = "Internal Server Error";

// Snatched from OpenSSL includes. I put the prototypes here to be independent
// from the OpenSSL source installation. Having this, mongoose + SSL can be
// built on any system with binary SSL libraries installed.
typedef struct ssl_st SSL;
typedef struct ssl_method_st SSL_METHOD;
typedef struct ssl_ctx_st SSL_CTX;

#define SSL_ERROR_WANT_READ 2
#define SSL_ERROR_WANT_WRITE 3
#define SSL_FILETYPE_PEM 1
#define CRYPTO_LOCK  1

#if defined(NO_SSL_DL)
extern void SSL_free(SSL *);
extern int SSL_accept(SSL *);
extern int SSL_connect(SSL *);
extern int SSL_read(SSL *, void *, int);
extern int SSL_write(SSL *, const void *, int);
extern int SSL_get_error(const SSL *, int);
extern int SSL_set_fd(SSL *, int);
extern SSL *SSL_new(SSL_CTX *);
extern SSL_CTX *SSL_CTX_new(SSL_METHOD *);
extern SSL_METHOD *SSLv23_server_method(void);
extern int SSL_library_init(void);
extern void SSL_load_error_strings(void);
extern int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int);
extern int SSL_CTX_use_certificate_file(SSL_CTX *, const char *, int);
extern int SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *);
extern void SSL_CTX_set_default_passwd_cb(SSL_CTX *, mg_callback_t);
extern void SSL_CTX_free(SSL_CTX *);
extern unsigned long ERR_get_error(void);
extern char *ERR_error_string(unsigned long, char *);
extern int CRYPTO_num_locks(void);
extern void CRYPTO_set_locking_callback(void (*)(int, int, const char *, int));
extern void CRYPTO_set_id_callback(unsigned long (*)(void));
#else
// Dynamically loaded SSL functionality
struct ssl_func {
    const char *name;   // SSL function name
    void  (*ptr)(void); // Function pointer
};

#define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
#define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
#define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr)
#define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr)
#define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr)
#define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5].ptr)
#define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)
#define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
#define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
#define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
#define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr)
#define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \
                const char *, int)) ssl_sw[11].ptr)
#define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
                const char *, int)) ssl_sw[12].ptr)
#define SSL_CTX_set_default_passwd_cb \
    (* (void (*)(SSL_CTX *, mg_callback_t)) ssl_sw[13].ptr)
#define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
#define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
#define SSL_CTX_use_certificate_chain_file \
    (* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr)

#define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr)
#define CRYPTO_set_locking_callback \
    (* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr)
#define CRYPTO_set_id_callback \
    (* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)
#define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr)
#define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr)

// set_ssl_option() function updates this array.
// It loads SSL library dynamically and changes NULLs to the actual addresses
// of respective functions. The macros above (like SSL_connect()) are really
// just calling these functions indirectly via the pointer.
static struct ssl_func ssl_sw[] = {
    {"SSL_free",   NULL},
    {"SSL_accept",   NULL},
    {"SSL_connect",   NULL},
    {"SSL_read",   NULL},
    {"SSL_write",   NULL},
    {"SSL_get_error",  NULL},
    {"SSL_set_fd",   NULL},
    {"SSL_new",   NULL},
    {"SSL_CTX_new",   NULL},
    {"SSLv23_server_method", NULL},
    {"SSL_library_init",  NULL},
    {"SSL_CTX_use_PrivateKey_file", NULL},
    {"SSL_CTX_use_certificate_file",NULL},
    {"SSL_CTX_set_default_passwd_cb",NULL},
    {"SSL_CTX_free",  NULL},
    {"SSL_load_error_strings", NULL},
    {"SSL_CTX_use_certificate_chain_file", NULL},
    {NULL,    NULL}
};

// Similar array as ssl_sw. These functions could be located in different lib.
static struct ssl_func crypto_sw[] = {
    {"CRYPTO_num_locks",  NULL},
    {"CRYPTO_set_locking_callback", NULL},
    {"CRYPTO_set_id_callback", NULL},
    {"ERR_get_error",  NULL},
    {"ERR_error_string", NULL},
    {NULL,    NULL}
};
#endif // NO_SSL_DL

static const char *month_names[] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

// Unified socket address. For IPv6 support, add IPv6 address structure
// in the union u.
struct usa {
    socklen_t len;
    union {
        struct sockaddr sa;
        struct sockaddr_in sin;
    } u;
};

// Describes a string (chunk of memory).
struct vec {
    const char *ptr;
    size_t len;
};

// Structure used by mg_stat() function. Uses 64 bit file length.
struct mgstat {
    int is_directory;  // Directory marker
    int64_t size;      // File size
    time_t mtime;      // Modification time
};

// Describes listening socket, or socket which was accept()-ed by the master
// thread and queued for future handling by the worker thread.
struct socket {
    struct socket *next;  // Linkage
    SOCKET sock;          // Listening socket
    struct usa lsa;       // Local socket address
    struct usa rsa;       // Remote socket address
    int is_ssl;           // Is socket SSL-ed
    int is_proxy;
};

enum {
    CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER,
    PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS, ACCESS_LOG_FILE,
    SSL_CHAIN_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE,
    GLOBAL_PASSWORDS_FILE, INDEX_FILES,
    ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST, MAX_REQUEST_SIZE,
    EXTRA_MIME_TYPES, LISTENING_PORTS,
    DOCUMENT_ROOT, SSL_CERTIFICATE, NUM_THREADS, RUN_AS_USER,
    NUM_OPTIONS
};

static const char *config_options[] = {
    "C", "cgi_extensions", ".cgi,.pl,.php",
    "E", "cgi_environment", NULL,
    "G", "put_delete_passwords_file", NULL,
    "I", "cgi_interpreter", NULL,
    "P", "protect_uri", NULL,
    "R", "authentication_domain", "mydomain.com",
    "S", "ssi_extensions", ".shtml,.shtm",
    "a", "access_log_file", NULL,
    "c", "ssl_chain_file", NULL,
    "d", "enable_directory_listing", "yes",
    "e", "error_log_file", NULL,
    "g", "global_passwords_file", NULL,
    "i", "index_files", "index.html,index.htm,index.cgi",
    "k", "enable_keep_alive", "no",
    "l", "access_control_list", NULL,
    "M", "max_request_size", "16384",
    "m", "extra_mime_types", NULL,
    "p", "listening_ports", "8080",
    "r", "document_root",  ".",
    "s", "ssl_certificate", NULL,
    "t", "num_threads", "10",
    "u", "run_as_user", NULL,
    NULL
};
#define ENTRIES_PER_CONFIG_OPTION 3

struct mg_context {
    volatile int stop_flag;       // Should we stop event loop
    SSL_CTX *ssl_ctx;             // SSL context
    char *config[NUM_OPTIONS];    // Mongoose configuration parameters
    mg_callback_t user_callback;  // User-defined callback function
    void *user_data;              // User-defined data

    struct socket *listening_sockets;

    volatile int num_threads;  // Number of threads
    pthread_mutex_t mutex;     // Protects (max|num)_threads
    pthread_cond_t  cond;      // Condvar for tracking workers terminations

    struct socket queue[20];   // Accepted sockets
    volatile int sq_head;      // Head of the socket queue
    volatile int sq_tail;      // Tail of the socket queue
    pthread_cond_t sq_full;    // Singaled when socket is produced
    pthread_cond_t sq_empty;   // Signaled when socket is consumed
};

struct mg_connection {
    struct mg_connection *peer; // Remote target in proxy mode
    struct mg_request_info request_info;
    struct mg_context *ctx;
    SSL *ssl;                   // SSL descriptor
    struct socket client;       // Connected client
    time_t birth_time;          // Time connection was accepted
    int64_t num_bytes_sent;     // Total bytes sent to client
    int64_t content_len;        // Content-Length header value
    int64_t consumed_content;   // How many bytes of content is already read
    char *buf;                  // Buffer for received data
    int buf_size;               // Buffer size
    int request_len;            // Size of the request + headers in a buffer
    int data_len;               // Total size of data in a buffer
};

const char **mg_get_valid_option_names(void) {
    return config_options;
}

static void *call_user(struct mg_connection *conn, enum mg_event event) {
    conn->request_info.user_data = conn->ctx->user_data;
    return conn->ctx->user_callback == NULL ? NULL :
        conn->ctx->user_callback(event, conn, &conn->request_info);
}

static int get_option_index(const char *name) {
    int i;

    for (i = 0; config_options[i] != NULL; i += ENTRIES_PER_CONFIG_OPTION) {
        if (strcmp(config_options[i], name) == 0 ||
                strcmp(config_options[i + 1], name) == 0) {
            return i / ENTRIES_PER_CONFIG_OPTION;
        }
    }
    return -1;
}

const char *mg_get_option(const struct mg_context *ctx, const char *name) {
    int i;
    if ((i = get_option_index(name)) == -1) {
        return NULL;
    } else if (ctx->config[i] == NULL) {
        return "";
    } else {
        return ctx->config[i];
    }
}

// Print error message to the opened error log stream.
static void cry(struct mg_connection *conn, const char *fmt, ...) {
    char buf[BUFSIZ];
    va_list ap;
    FILE *fp;
    time_t timestamp;

    va_start(ap, fmt);
    (void) vsnprintf(buf, sizeof(buf), fmt, ap);
    va_end(ap);

    // Do not lock when getting the callback value, here and below.
    // I suppose this is fine, since function cannot disappear in the
    // same way string option can.
    conn->request_info.log_message = buf;
    if (call_user(conn, MG_EVENT_LOG) == NULL) {
        fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
            mg_fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");

        if (fp != NULL) {
            flockfile(fp);
            timestamp = time(NULL);

            (void) fprintf(fp,
                    "[%010lu] [error] [client %s] ",
                    (unsigned long) timestamp,
                    inet_ntoa(conn->client.rsa.u.sin.sin_addr));

            if (conn->request_info.request_method != NULL) {
                (void) fprintf(fp, "%s %s: ",
                        conn->request_info.request_method,
                        conn->request_info.uri);
            }

            (void) fprintf(fp, "%s", buf);
            fputc('\n', fp);
            funlockfile(fp);
            if (fp != stderr) {
                fclose(fp);
            }
        }
    }
    conn->request_info.log_message = NULL;
}

// Return OpenSSL error message
static const char *ssl_error(void) {
    unsigned long err;
    err = ERR_get_error();
    return err == 0 ? "" : ERR_error_string(err, NULL);
}

// Return fake connection structure. Used for logging, if connection
// is not applicable at the moment of logging.
static struct mg_connection *fc(struct mg_context *ctx) {
    static struct mg_connection fake_connection;
    fake_connection.ctx = ctx;
    return &fake_connection;
}

const char *mg_version(void) {
    return MONGOOSE_VERSION;
}

static void mg_strlcpy(register char *dst, register const char *src, size_t n) {
    for (; *src != '\0' && n > 1; n--) {
        *dst++ = *src++;
    }
    *dst = '\0';
}

static int lowercase(const char *s) {
    return tolower(* (const unsigned char *) s);
}

static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
    int diff = 0;

    if (len > 0)
        do {
            diff = lowercase(s1++) - lowercase(s2++);
        } while (diff == 0 && s1[-1] != '\0' && --len > 0);

    return diff;
}

static int mg_strcasecmp(const char *s1, const char *s2) {
    int diff;

    do {
        diff = lowercase(s1++) - lowercase(s2++);
    } while (diff == 0 && s1[-1] != '\0');

    return diff;
}

static char * mg_strndup(const char *ptr, size_t len) {
    char *p;

    if ((p = (char *) malloc(len + 1)) != NULL) {
        mg_strlcpy(p, ptr, len + 1);
    }

    return p;
}

static char * mg_strdup(const char *str) {
    return mg_strndup(str, strlen(str));
}

// Like snprintf(), but never returns negative value, or the value
// that is larger than a supplied buffer.
// Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
// in his audit report.
static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen,
        const char *fmt, va_list ap) {
    int n;

    if (buflen == 0)
        return 0;

    n = vsnprintf(buf, buflen, fmt, ap);

    if (n < 0) {
        cry(conn, "vsnprintf error");
        n = 0;
    } else if (n >= (int) buflen) {
        cry(conn, "truncating vsnprintf buffer: [%.*s]",
                n > 200 ? 200 : n, buf);
        n = (int) buflen - 1;
    }
    buf[n] = '\0';

    return n;
}

static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
        const char *fmt, ...) {
    va_list ap;
    int n;

    va_start(ap, fmt);
    n = mg_vsnprintf(conn, buf, buflen, fmt, ap);
    va_end(ap);

    return n;
}

// Skip the characters until one of the delimiters characters found.
// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
// Advance pointer to buffer to the next word. Return found 0-terminated word.
// Delimiters can be quoted with quotechar.
static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar) {
    char *p, *begin_word, *end_word, *end_whitespace;

    begin_word = *buf;
    end_word = begin_word + strcspn(begin_word, delimiters);

    /* Check for quotechar */
    if (end_word > begin_word) {
        p = end_word - 1;
        while (*p == quotechar) {
            /* If there is anything beyond end_word, copy it */
            if (*end_word == '\0') {
                *p = '\0';
                break;
            } else {
                size_t end_off = strcspn(end_word + 1, delimiters);
                memmove (p, end_word, end_off + 1);
                p += end_off; /* p must correspond to end_word - 1 */
                end_word += end_off + 1;
            }
        }
        for (p++; p < end_word; p++) {
            *p = '\0';
        }
    }

    if (*end_word == '\0') {
        *buf = end_word;
    } else {
        end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);

        for (p = end_word; p < end_whitespace; p++) {
            *p = '\0';
        }

        *buf = end_whitespace;
    }

    return begin_word;
}

// Simplified version of skip_quoted without quote char
// and whitespace == delimiters
static char *skip(char **buf, const char *delimiters) {
    return skip_quoted(buf, delimiters, delimiters, 0);
}


// Return HTTP header value, or NULL if not found.
static const char *get_header(const struct mg_request_info *ri,
        const char *name) {
    int i;

    for (i = 0; i < ri->num_headers; i++)
        if (!mg_strcasecmp(name, ri->http_headers[i].name))
            return ri->http_headers[i].value;

    return NULL;
}

const char *mg_get_header(const struct mg_connection *conn, const char *name) {
    return get_header(&conn->request_info, name);
}

// A helper function for traversing comma separated list of values.
// It returns a list pointer shifted to the next value, of NULL if the end
// of the list found.
// Value is stored in val vector. If value has form "x=y", then eq_val
// vector is initialized to point to the "y" part, and val vector length
// is adjusted to point only to "x".
static const char *next_option(const char *list, struct vec *val,
        struct vec *eq_val) {
    if (list == NULL || *list == '\0') {
        /* End of the list */
        list = NULL;
    } else {
        val->ptr = list;
        if ((list = strchr(val->ptr, ',')) != NULL) {
            /* Comma found. Store length and shift the list ptr */
            val->len = list - val->ptr;
            list++;
        } else {
            /* This value is the last one */
            list = val->ptr + strlen(val->ptr);
            val->len = list - val->ptr;
        }

        if (eq_val != NULL) {
            /*
             * Value has form "x=y", adjust pointers and lengths
             * so that val points to "x", and eq_val points to "y".
             */
            eq_val->len = 0;
            eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len);
            if (eq_val->ptr != NULL) {
                eq_val->ptr++;  /* Skip over '=' character */
                eq_val->len = val->ptr + val->len - eq_val->ptr;
                val->len = (eq_val->ptr - val->ptr) - 1;
            }
        }
    }

    return list;
}

#if !defined(NO_CGI)
static int match_extension(const char *path, const char *ext_list) {
    struct vec ext_vec;
    size_t path_len;

    path_len = strlen(path);

    while ((ext_list = next_option(ext_list, &ext_vec, NULL)) != NULL)
        if (ext_vec.len < path_len &&
                mg_strncasecmp(path + path_len - ext_vec.len,
                    ext_vec.ptr, ext_vec.len) == 0)
            return 1;

    return 0;
}
#endif // !NO_CGI

// HTTP 1.1 assumes keep alive if "Connection:" header is not set
// This function must tolerate situations when connection info is not
// set up, for example if request parsing failed.
static int should_keep_alive(const struct mg_connection *conn) {
    const char *http_version = conn->request_info.http_version;
    const char *header = mg_get_header(conn, "Connection");
    return (header == NULL && http_version && !strcmp(http_version, "1.1")) ||
        (header != NULL && !mg_strcasecmp(header, "keep-alive"));
}

static const char *suggest_connection_header(const struct mg_connection *conn) {
    return should_keep_alive(conn) ? "keep-alive" : "close";
}

static void send_http_error(struct mg_connection *conn, int status,
        const char *reason, const char *fmt, ...) {
    char buf[BUFSIZ];
    va_list ap;
    int len;

    conn->request_info.status_code = status;

    if (call_user(conn, MG_HTTP_ERROR) == NULL) {
        buf[0] = '\0';
        len = 0;

        /* Errors 1xx, 204 and 304 MUST NOT send a body */
        if (status > 199 && status != 204 && status != 304) {
            len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
            cry(conn, "%s", buf);
            buf[len++] = '\n';

            va_start(ap, fmt);
            len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap);
            va_end(ap);
        }
        DEBUG_TRACE(("[%s]", buf));

        mg_printf(conn, "HTTP/1.1 %d %s\r\n"
                "Content-Type: text/plain\r\n"
                "Content-Length: %d\r\n"
                "Connection: %s\r\n\r\n", status, reason, len,
                suggest_connection_header(conn));
        conn->num_bytes_sent += mg_printf(conn, "%s", buf);
    }
}
